#!/usr/bin/env python
#
# Copyright 2020 Caoyingjun
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# https://github.com/kubernetes-client/python/

import os

from ansible.module_utils.basic import AnsibleModule
import docker
from kubernetes import client
from kubernetes import config

KUBECONFIG = '/etc/kubernetes/admin.conf'
KUBEZIMAGE = 'jacky06/kubez-nginx:v1.0.0'
KUBEZPATH = '/var/lib/kubez-nginx'


def template_kubez_nginx(kubez_path, lb_vip, lb_port, node_port, endpoints):
    ep_entries = []
    for endpoint in endpoints:
        ep_entry = ('        server {ep}:{np}  '
                    'max_fails=3 '
                    'fail_timeout=30s;\n'.format(ep=endpoint,
                                                 np=node_port))
        ep_entries.append(ep_entry)
    ep_entries = ''.join(ep_entries).strip()

    nginx_content = '''#NOTE(caoyingjun): Generated by kubez template.
worker_processes 1;
events {{
    worker_connections  1024;
}}
stream {{
    upstream backend {{
        hash $remote_addr consistent;
        {ep_entries}
    }}
    server {{
        listen {lb_vip}:{lb_port};
        proxy_connect_timeout 1s;
        proxy_pass backend;
    }}
}}'''.format(ep_entries=ep_entries, lb_vip=lb_vip, lb_port=lb_port)

    with open(os.path.join(kubez_path, 'kubez-nginx.conf'), 'w') as f:
        f.write(nginx_content)


def get_docker_client():
    return docker.APIClient(version='auto')


class KubeService(object):

    def __init__(self, name, namespace, externalip):
        self.name = name
        self.namespace = namespace
        self.externalip = externalip

        # Ensure the kubez path exits
        self.create_kubez_path()
        # Configs can be set in Configuration class directly or using helper utility
        config.load_kube_config()

        self.v1client = client.CoreV1Api()
        self.dc = get_docker_client()

    def create_kubez_path(self):
        if not os.path.exists(KUBEZPATH):
            os.makedirs(KUBEZPATH)

    @property
    def get_node_ips(self):
        nodes = self.v1client.list_node()
        node_ips = []
        for node in nodes.items:
            addresses = node.status.addresses
            for address in addresses:
                if address.type == 'InternalIP':
                    node_ips.append(address.address)
        return node_ips

    def get_namespaced_service(self, name, namespace):
        service = self.v1client.read_namespaced_service(name, namespace)
        return service

    def brefore_update_namespaced_service(self):
        self.restart_container(self.name, KUBEZPATH)

    def update_namespaced_service(self):
        self.brefore_update_namespaced_service()

        body = {
            'spec': {
                'externalName': 'kubez',
                'externalIPs': [self.externalip],
                'type': 'LoadBalancer'
                }
        }
        self.v1client.patch_namespaced_service(self.name, self.namespace, body)

    def restart_container(self, name, volume):
        try:
            self.dc.remove_container(name, force=True)
        except Exception:
            pass

        self.create_container(name, volume)
        self.dc.restart(name)

    def before_create_container(self):
        service = self.get_namespaced_service(self.name, self.namespace)
        ports = service.spec.ports
        # TODO: just hanlder the frist ports for now
        for port in ports:
            template_kubez_nginx(KUBEZPATH,
                                 self.externalip,
                                 port.port,
                                 port.node_port,
                                 self.get_node_ips[:3])
            break

        self.dc.pull(KUBEZIMAGE)

    def create_container(self, name, volume):
        self.before_create_container()

        # containers = self.dc.containers()
        # for container in containers:
        options = {
            'restart_policy': {
                'Name': 'always'
            },
            'network_mode': 'host',
            'binds': {
                volume: {
                    'bind': '/etc/kubez-nginx',
                    'mode': 'rw',
                },
            },
        }

        host_config = self.dc.create_host_config(**options)
        self.dc.create_container(
            **{'name': name, 'image': KUBEZIMAGE, 'host_config': host_config})


def main():
    # specs = dict(
    #     name=dict(required=True, type='str'),
    #     namespace=dict(required=False, type='str', default='default'),
    #     externalip=dict(required=True, type='str'),
    # )
    # module = AnsibleModule(argument_spec=specs, bypass_checks=True)
    # params = module.params

    ks = KubeService('ingress-nginx', 'kube-system', '103.39.211.122')

    try:
        ks.update_namespaced_service()
    except Exception as e:
        print(e)


if __name__ == '__main__':
    main()
